/*=====================================================
Critical Links
Auto Configuration Server

Authors:
	Rui Eduardo Gouveia Gil - r-gil@critical-links.com

Date: 2010
Version 0.1
=======================================================*/
#include <pion/net/HTTPResponseWriter.hpp>
#include "httpd/HttpUserManager.h"
#include "httpd/SConfigurations.h"
#include "httpd/HttpJavaScript.h"
#include "httpd/SDevices.h"
#include "httpd/SAccount.h"
#include "httpd/SIndex.h"
#include "httpd/SGui.h"

namespace acs
{

rgcpp::Map<std::string,boost::shared_ptr<Invoker> > HttpJavaScript::javaScriptMap;

HttpJavaScript::HttpJavaScript()
{
	HttpJavaScript::initJavaScriptMap();
}

HttpJavaScript::~HttpJavaScript()
{

}

void HttpJavaScript::operator()(pion::net::HTTPRequestPtr& request, pion::net::TCPConnectionPtr& tcpConn)
{
	HttpUserThreadBind httpUserThreadBind(request->getUser());

	if(request->getMethod().compare("POST") == 0)
	{
		std::string args;
		std::string methodName;
		std::vector<std::string> arguments;


		methodName = request->getQuery("method");
		for(int i=1; i<request->getQueryParams().size(); i++)
		{
			arguments.push_back(request->getQuery(std::string("param")+rgcpp::ToString(i)));
			args += request->getQuery(std::string("param")+rgcpp::ToString(i)) + ",";
		}

		if(!args.empty())
			args.resize(args.size() - 1);

		LOG_DEBUG_DATA(methodName << "(" << args << ")");

		if(HttpJavaScript::javaScriptMap.find(methodName) != HttpJavaScript::javaScriptMap.end())
		{
			std::stringstream stream;
			std::vector<std::string> vector = HttpJavaScript::javaScriptMap[methodName]->call(arguments);

			if(vector.size() == 1)
			{
				stream << "unescape(\"" << rgcpp::Encoder::encode(vector[0]) << "\")";
			}
			else if(vector.size() > 1)
			{
				stream << "[";
				for(std::vector<std::string>::iterator str = vector.begin(); str != vector.end();)
				{
					stream << "unescape(\"" <<  rgcpp::Encoder::encode(*str) << "\")";
					str++;
					if(str != vector.end())
						stream << ",";
				}
				stream << "]";
			}

			std::string result = stream.str();
			LOG_DEBUG_DATA(result);

			pion::net::HTTPResponseWriterPtr writer(pion::net::HTTPResponseWriter::create(tcpConn, *request, boost::bind(&pion::net::TCPConnection::finish, tcpConn)));
			writer->getResponse().setContentType("text/plain");

			writer->write(result);
			writer->write(pion::net::HTTPTypes::STRING_CRLF);
			writer->write(pion::net::HTTPTypes::STRING_CRLF);
			writer->send();
		}
		else
		{
			static const std::string DENY_HTML = "Invalid Http Request.";
			pion::net::HTTPResponseWriterPtr writer(pion::net::HTTPResponseWriter::create(tcpConn, *request, boost::bind(&pion::net::TCPConnection::finish, tcpConn)));
			writer->getResponse().setStatusCode(pion::net::HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED);
			writer->getResponse().setStatusMessage(pion::net::HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);

			writer->writeNoCopy(DENY_HTML);
			writer->writeNoCopy(pion::net::HTTPTypes::STRING_CRLF);
			writer->writeNoCopy(pion::net::HTTPTypes::STRING_CRLF);
			writer->send();
		}
	}
	else
	{
		static const std::string DENY_HTML = "<html><body>Invalid Http Method. Only POST request are available</body></html>";
		pion::net::HTTPResponseWriterPtr writer(pion::net::HTTPResponseWriter::create(tcpConn, *request, boost::bind(&pion::net::TCPConnection::finish, tcpConn)));
		writer->getResponse().setStatusCode(pion::net::HTTPTypes::RESPONSE_CODE_METHOD_NOT_ALLOWED);
		writer->getResponse().setStatusMessage(pion::net::HTTPTypes::RESPONSE_MESSAGE_METHOD_NOT_ALLOWED);

		writer->writeNoCopy(DENY_HTML);
		writer->writeNoCopy(pion::net::HTTPTypes::STRING_CRLF);
		writer->writeNoCopy(pion::net::HTTPTypes::STRING_CRLF);
		writer->send();
	}
}

void HttpJavaScript::initJavaScriptMap()
{
	LOG_TRACE_STR("Initializing JavaScript Map...");

	if(!HttpJavaScript::javaScriptMap.empty())
		return;

	HttpJavaScript::javaScriptMap["SIndex.getHtml"].reset(new InvokerInterface<typeof(&SIndex::getHtml)>(&SIndex::getHtml,"SIndex","getHtml"));
	HttpJavaScript::javaScriptMap["SIndex.getDevices"].reset(new InvokerInterface<typeof(&SIndex::getDevices)>(&SIndex::getDevices,"SIndex","getDevices"));
	HttpJavaScript::javaScriptMap["SIndex.getLogs"].reset(new InvokerInterface<typeof(&SIndex::getLogs)>(&SIndex::getLogs,"SIndex","getLogs"));

	HttpJavaScript::javaScriptMap["SDevices.getHtml"].reset(new InvokerInterface<typeof(&SDevices::getHtml)>(&SDevices::getHtml,"SDevices","getHtml"));
	HttpJavaScript::javaScriptMap["SDevices.getDevices"].reset(new InvokerInterface<typeof(&SDevices::getDevices)>(&SDevices::getDevices,"SDevices","getDevices"));
	HttpJavaScript::javaScriptMap["SDevices.getLogs"].reset(new InvokerInterface<typeof(&SDevices::getLogs)>(&SDevices::getLogs,"SDevices","getLogs"));
	HttpJavaScript::javaScriptMap["SDevices.createDevice"].reset(new InvokerInterface<typeof(&SDevices::createDevice)>(&SDevices::createDevice,"SDevices","createDevice"));
	HttpJavaScript::javaScriptMap["SDevices.saveDevice"].reset(new InvokerInterface<typeof(&SDevices::saveDevice)>(&SDevices::saveDevice,"SDevices","saveDevice"));
	HttpJavaScript::javaScriptMap["SDevices.validateDevice"].reset(new InvokerInterface<typeof(&SDevices::validateDevice)>(&SDevices::validateDevice,"SDevices","validateDevice"));
	HttpJavaScript::javaScriptMap["SDevices.removeDevice"].reset(new InvokerInterface<typeof(&SDevices::removeDevice)>(&SDevices::removeDevice,"SDevices","removeDevice"));
	HttpJavaScript::javaScriptMap["SDevices.clearDeviceBackupRestoreFile"].reset(new InvokerInterface<typeof(&SDevices::clearDeviceBackupRestoreFile)>(&SDevices::clearDeviceBackupRestoreFile,"SDevices","clearDeviceBackupRestoreFile"));
	HttpJavaScript::javaScriptMap["SDevices.addDeviceCommand"].reset(new InvokerInterface<typeof(&SDevices::addDeviceCommand)>(&SDevices::addDeviceCommand,"SDevices","addDeviceCommand"));
	HttpJavaScript::javaScriptMap["SDevices.switchDeviceCommand"].reset(new InvokerInterface<typeof(&SDevices::switchDeviceCommand)>(&SDevices::switchDeviceCommand,"SDevices","switchDeviceCommand"));
	HttpJavaScript::javaScriptMap["SDevices.removeDeviceCommand"].reset(new InvokerInterface<typeof(&SDevices::removeDeviceCommand)>(&SDevices::removeDeviceCommand,"SDevices","removeDeviceCommand"));
	HttpJavaScript::javaScriptMap["SDevices.generateConfiguration"].reset(new InvokerInterface<typeof(&SDevices::generateConfiguration)>(&SDevices::generateConfiguration,"SDevices","generateConfiguration"));
	HttpJavaScript::javaScriptMap["SDevices.getLogs"].reset(new InvokerInterface<typeof(&SDevices::getLogs)>(&SDevices::getLogs,"SDevices","getLogs"));

	HttpJavaScript::javaScriptMap["SConfigurations.getHtml"].reset(new InvokerInterface<typeof(&SConfigurations::getHtml)>(&SConfigurations::getHtml,"SConfigurations","getHtml"));
	HttpJavaScript::javaScriptMap["SConfigurations.createConfiguration"].reset(new InvokerInterface<typeof(&SConfigurations::createConfiguration)>(&SConfigurations::createConfiguration,"SConfigurations","createConfiguration"));
	HttpJavaScript::javaScriptMap["SConfigurations.saveConfiguration"].reset(new InvokerInterface<typeof(&SConfigurations::saveConfiguration)>(&SConfigurations::saveConfiguration,"SConfigurations","saveConfiguration"));
	HttpJavaScript::javaScriptMap["SConfigurations.removeConfiguration"].reset(new InvokerInterface<typeof(&SConfigurations::removeConfiguration)>(&SConfigurations::removeConfiguration,"SConfigurations","removeConfiguration"));
	HttpJavaScript::javaScriptMap["SConfigurations.clearConfigurationBackupRestoreFile"].reset(new InvokerInterface<typeof(&SConfigurations::clearConfigurationBackupRestoreFile)>(&SConfigurations::clearConfigurationBackupRestoreFile,"SConfigurations","clearConfigurationBackupRestoreFile"));
	HttpJavaScript::javaScriptMap["SConfigurations.addConfigurationCommand"].reset(new InvokerInterface<typeof(&SConfigurations::addConfigurationCommand)>(&SConfigurations::addConfigurationCommand,"SConfigurations","addConfigurationCommand"));
	HttpJavaScript::javaScriptMap["SConfigurations.switchConfigurationCommand"].reset(new InvokerInterface<typeof(&SConfigurations::switchConfigurationCommand)>(&SConfigurations::switchConfigurationCommand,"SConfigurations","switchConfigurationCommand"));
	HttpJavaScript::javaScriptMap["SConfigurations.removeConfigurationCommand"].reset(new InvokerInterface<typeof(&SConfigurations::removeConfigurationCommand)>(&SConfigurations::removeConfigurationCommand,"SConfigurations","removeConfigurationCommand"));

	HttpJavaScript::javaScriptMap["SAccount.getMenuHtml"].reset(new InvokerInterface<typeof(&SAccount::getMenuHtml)>(&SAccount::getMenuHtml,"SAccount","getMenuHtml"));
	HttpJavaScript::javaScriptMap["SAccount.getHtml"].reset(new InvokerInterface<typeof(&SAccount::getHtml)>(&SAccount::getHtml,"SAccount","getHtml"));
	HttpJavaScript::javaScriptMap["SAccount.createUser"].reset(new InvokerInterface<typeof(&SAccount::createUser)>(&SAccount::createUser,"SAccount","createUser"));
	HttpJavaScript::javaScriptMap["SAccount.saveUser"].reset(new InvokerInterface<typeof(&SAccount::saveUser)>(&SAccount::saveUser,"SAccount","saveUser"));
	HttpJavaScript::javaScriptMap["SAccount.removeUser"].reset(new InvokerInterface<typeof(&SAccount::removeUser)>(&SAccount::removeUser,"SAccount","removeUser"));

	HttpJavaScript::javaScriptMap["SGui.createGroup"].reset(new InvokerInterface<typeof(&SGui::createGroup)>(&SGui::createGroup,"SGui","createGroup"));
	HttpJavaScript::javaScriptMap["SGui.saveGroup"].reset(new InvokerInterface<typeof(&SGui::saveGroup)>(&SGui::saveGroup,"SGui","saveGroup"));
	HttpJavaScript::javaScriptMap["SGui.getGroup"].reset(new InvokerInterface<typeof(&SGui::getGroup)>(&SGui::getGroup,"SGui","getGroup"));
	HttpJavaScript::javaScriptMap["SGui.getGroups"].reset(new InvokerInterface<typeof(&SGui::getGroups)>(&SGui::getGroups,"SGui","getGroups"));
	HttpJavaScript::javaScriptMap["SGui.removeGroup"].reset(new InvokerInterface<typeof(&SGui::removeGroup)>(&SGui::removeGroup,"SGui","removeGroup"));

	HttpJavaScript::javaScriptMap["SGui.createConfiguration"].reset(new InvokerInterface<typeof(&SGui::createConfiguration)>(&SGui::createConfiguration,"SGui","createConfiguration"));
	HttpJavaScript::javaScriptMap["SGui.saveConfiguration"].reset(new InvokerInterface<typeof(&SGui::saveConfiguration)>(&SGui::saveConfiguration,"SGui","saveConfiguration"));
	HttpJavaScript::javaScriptMap["SGui.clearConfigurationBackupRestoreFile"].reset(new InvokerInterface<typeof(&SGui::clearConfigurationBackupRestoreFile)>(&SGui::clearConfigurationBackupRestoreFile,"SGui","clearConfigurationBackupRestoreFile"));
	HttpJavaScript::javaScriptMap["SGui.getConfiguration"].reset(new InvokerInterface<typeof(&SGui::getConfiguration)>(&SGui::getConfiguration,"SGui","getConfiguration"));
	HttpJavaScript::javaScriptMap["SGui.getConfigurations"].reset(new InvokerInterface<typeof(&SGui::getConfigurations)>(&SGui::getConfigurations,"SGui","getConfigurations"));
	HttpJavaScript::javaScriptMap["SGui.removeConfiguration"].reset(new InvokerInterface<typeof(&SGui::removeConfiguration)>(&SGui::removeConfiguration,"SGui","removeConfiguration"));

	HttpJavaScript::javaScriptMap["SGui.createDevice"].reset(new InvokerInterface<typeof(&SGui::createDevice)>(&SGui::createDevice,"SGui","createDevice"));
	HttpJavaScript::javaScriptMap["SGui.saveDevice"].reset(new InvokerInterface<typeof(&SGui::saveDevice)>(&SGui::saveDevice,"SGui","saveDevice"));
	HttpJavaScript::javaScriptMap["SGui.validateDevice"].reset(new InvokerInterface<typeof(&SGui::validateDevice)>(&SGui::validateDevice,"SGui","validateDevice"));
	HttpJavaScript::javaScriptMap["SGui.backupDevice"].reset(new InvokerInterface<typeof(&SGui::backupDevice)>(&SGui::backupDevice,"SGui","backupDevice"));
	HttpJavaScript::javaScriptMap["SGui.getDevice"].reset(new InvokerInterface<typeof(&SGui::getDevice)>(&SGui::getDevice,"SGui","getDevice"));
	HttpJavaScript::javaScriptMap["SGui.getDevices"].reset(new InvokerInterface<typeof(&SGui::getDevices)>(&SGui::getDevices,"SGui","getDevices"));
	HttpJavaScript::javaScriptMap["SGui.getGroupDevices"].reset(new InvokerInterface<typeof(&SGui::getGroupDevices)>(&SGui::getGroupDevices,"SGui","getGroupDevices"));
	HttpJavaScript::javaScriptMap["SGui.getConfigurationDevices"].reset(new InvokerInterface<typeof(&SGui::getConfigurationDevices)>(&SGui::getConfigurationDevices,"SGui","getConfigurationDevices"));
	HttpJavaScript::javaScriptMap["SGui.removeDevice"].reset(new InvokerInterface<typeof(&SGui::removeDevice)>(&SGui::removeDevice,"SGui","removeDevice"));

	HttpJavaScript::javaScriptMap["SGui.setDeviceCommands"].reset(new InvokerInterface<typeof(&SGui::setDeviceCommands)>(&SGui::setDeviceCommands,"SGui","setDeviceCommands"));
	HttpJavaScript::javaScriptMap["SGui.addDeviceCommand"].reset(new InvokerInterface<typeof(&SGui::addDeviceCommand)>(&SGui::addDeviceCommand,"SGui","addDeviceCommand"));
	HttpJavaScript::javaScriptMap["SGui.getDeviceCommands"].reset(new InvokerInterface<typeof(&SGui::getDeviceCommands)>(&SGui::getDeviceCommands,"SGui","getDeviceCommands"));
	HttpJavaScript::javaScriptMap["SGui.removeDeviceCommand"].reset(new InvokerInterface<typeof(&SGui::removeDeviceCommand)>(&SGui::removeDeviceCommand,"SGui","removeDeviceCommand"));
	HttpJavaScript::javaScriptMap["SGui.removeDeviceCommands"].reset(new InvokerInterface<typeof(&SGui::removeDeviceCommands)>(&SGui::removeDeviceCommands,"SGui","removeDeviceCommands"));

	HttpJavaScript::javaScriptMap["SGui.moveDeviceToGroup"].reset(new InvokerInterface<typeof(&SGui::moveDeviceToGroup)>(&SGui::moveDeviceToGroup,"SGui","moveDeviceToGroup"));
	HttpJavaScript::javaScriptMap["SGui.applyConfigurationToGroups"].reset(new InvokerInterface<typeof(&SGui::applyConfigurationToGroups)>(&SGui::applyConfigurationToGroups,"SGui","applyConfigurationToGroups"));
	HttpJavaScript::javaScriptMap["SGui.applyConfigurationToDevices"].reset(new InvokerInterface<typeof(&SGui::applyConfigurationToDevices)>(&SGui::applyConfigurationToDevices,"SGui","applyConfigurationToDevices"));

	HttpJavaScript::javaScriptMap["SGui.getLogs"].reset(new InvokerInterface<typeof(&SGui::getLogs)>(&SGui::getLogs,"SGui","getLogs"));
	HttpJavaScript::javaScriptMap["SGui.getDeviceLogs"].reset(new InvokerInterface<typeof(&SGui::getDeviceLogs)>(&SGui::getDeviceLogs,"SGui","getDeviceLogs"));

	HttpJavaScript::javaScriptMap["SGui.changePassword"].reset(new InvokerInterface<typeof(&SGui::changePassword)>(&SGui::changePassword,"SGui","changePassword"));

	HttpJavaScript::generateJavaScriptFile();

	LOG_TRACE_STR("JavaScript Map Initialized.");
}

void HttpJavaScript::generateJavaScriptFile()
{
	std::ofstream file((ACSConfig::getWebDirectory()+"js/cdwr.js").c_str(),std::ios_base::binary | std::ios_base::out | std::ios_base::trunc);

	if(!file.good())
	{
		Throw(rgcpp::RuntimeException(std::string("Unable to generate javascript interface file: ")+std::string(" - invalid file path =")+(ACSConfig::getWebDirectory()+"js/cdwr.js")));
	}

	for(rgcpp::Map<std::string,boost::shared_ptr<Invoker> >::iterator itr = HttpJavaScript::javaScriptMap.begin(); itr != HttpJavaScript::javaScriptMap.end(); itr++)
		file << itr->second->generateJavaScript();

	file.close();
}

}

extern "C" PION_SERVICE_API acs::HttpJavaScript* pion_create_HttpJavaScript()
{
	return new acs::HttpJavaScript();
}

extern "C" PION_SERVICE_API void pion_destroy_HttpJavaScript(acs::HttpJavaScript *servicePtr)
{
	delete servicePtr;
}
